
#include "nane/ScriptSystem/ScriptModule.h"
#include "nane/ScriptSystem/ScriptModuleImpl_.h"
#include "nane/ScriptSystem/ScriptSystemImpl_.h"
#include "nane/ScriptSystem/ScriptObjectImpl_.h"

#include <algorithm>

namespace nane
{
    ////////////////////////////////////////////////////////////////////////////////////////////////
    ScriptModule::ScriptModule( const NullType& )
        : SubSystemProduct()
        , m( NULL )
    {
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    ScriptModule::ScriptModule( Implementation* _impl )
        : SubSystemProduct( _impl )
        , m( _impl )
    {
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    ScriptModule::ScriptModule( const ScriptModule& _other )
        : SubSystemProduct( _other )
        , m( _other.m )
    {
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    ScriptObject ScriptModule::ImportObject( const char* _objectName )
    {
        ScriptObject scriptObject = NullPtr;
        Implementation::TExportedFunctionVector::iterator it_find
                = std::find_if(m->exportedFunctions.begin(), m->exportedFunctions.end(), Implementation::ExportedFunctionFindName("", _objectName));
        if( it_find != m->exportedFunctions.end() )
        {
            scriptObject = it_find->function;
        }
        else if( m->interface != NULL )
        {
            IScriptObject* objectInterface = m->interface->ImportObject(_objectName);
            ScriptObject::Implementation* scriptObjectImpl = new ScriptObject::Implementation(m, objectInterface);
            scriptObject = ScriptObject(scriptObjectImpl);
        }
        return scriptObject;
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    ScriptObject ScriptModule::ExportFunctionInterface( const char* _className, const char* _functionName, IScriptFunction* _cfunction )
    {
        NANE_ASSERT_RV( _cfunction != NULL, "Script object must be valid", NullPtr );

        ScriptObject::Implementation* scriptObjectImpl = NULL;
        IScriptObject* objectInterface = m->interface->ExportFunction(_className, _functionName, _cfunction);
        
        if( objectInterface != NULL )
        {
            scriptObjectImpl = new ScriptObject::Implementation(m, objectInterface);

            Implementation::TExportedFunctionVector::iterator it_find
                    = std::find_if(m->exportedFunctions.begin(), m->exportedFunctions.end(), Implementation::ExportedFunctionFindName(_className, _functionName));
            if( it_find != m->exportedFunctions.end() )
            {
                LOG_WARNING("'%1.%2' function was already exported in module '%3'") << _className << _functionName << m->name.c_str();
                std::swap(*it_find, m->exportedFunctions.back());
                m->exportedFunctions.pop_back();
            }
        }
        return ScriptObject(scriptObjectImpl);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    bool ScriptModule::ExportFunctionImpl_( const char* _className, const char* _functionName, ScriptFunctionBase* _scriptFunctionBase )
    {
        std::string className = _className ? _className : "";
        std::string functionName = _functionName ? _functionName : "";
        ScriptObject scriptObject = ExportFunctionInterface(className.c_str(), functionName.c_str(), static_cast<IScriptFunction*>(_scriptFunctionBase));
        if( scriptObject != NullPtr )
        {
            Implementation::ExportedFunction exportedFunction = { className, functionName, scriptObject };
            m->exportedFunctions.push_back(exportedFunction);
        }
        return scriptObject != NullPtr;
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
}   // namespace nane
